home *** CD-ROM | disk | FTP | other *** search
/ SPACE 2 / SPACE - Library 2 - Volume 1.iso / demos / 198 / bind.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-08-20  |  23.7 KB  |  1,036 lines

  1. /*    This file is for functions having to do with key bindings,
  2.     descriptions, help commands and startup file.
  3.  
  4.     written 11-feb-86 by Daniel Lawrence
  5.                                 */
  6.  
  7. #include    <stdio.h>
  8. #include    "estruct.h"
  9. #include    "etype.h"
  10. #include    "edef.h"
  11. #include    "elang.h"
  12. #include    "epath.h"
  13.  
  14. PASCAL NEAR help(f, n)    /* give me some help!!!!
  15.            bring up a fake buffer and read the help file
  16.            into it with view mode            */
  17. {
  18.     register BUFFER *bp;    /* buffer pointer to help */
  19.     char *fname;        /* file name of help file */
  20.  
  21.     /* first check if we are already here */
  22.     bp = bfind("emacs.hlp", FALSE, BFINVS);
  23.  
  24.     if (bp == NULL) {
  25. #if SHARED
  26.             strcpy(tname, pathname[1]);
  27.             fname = flook(tname, FALSE);
  28. #else       
  29.         fname = flook(pathname[1], FALSE);
  30. #endif
  31.         if (fname == NULL) {
  32.             mlwrite(TEXT12);
  33. /*                              "[Help file is not online]" */
  34.             return(FALSE);
  35.         }
  36.     }
  37.  
  38.     /* split the current window to make room for the help stuff */
  39.     if (splitwind(FALSE, 1) == FALSE)
  40.             return(FALSE);
  41.  
  42.     if (bp == NULL) {
  43.         /* and read the stuff in */
  44.         if (getfile(fname, FALSE) == FALSE)
  45.             return(FALSE);
  46.     } else
  47.         swbuffer(bp);
  48.  
  49.     /* make this window in VIEW mode, update all mode lines */
  50.     curwp->w_bufp->b_mode |= MDVIEW;
  51.     curwp->w_bufp->b_flag |= BFINVS;
  52.     upmode();
  53.     return(TRUE);
  54. }
  55.  
  56. PASCAL NEAR deskey(f, n)    /* describe the command for a certain key */
  57.  
  58. {
  59.     register int c;        /* key to describe */
  60.     register char *ptr;    /* string pointer to scan output strings */
  61.     char outseq[NSTRING];    /* output buffer for command sequence */
  62.  
  63.     /* prompt the user to type us a key to describe */
  64.     mlwrite(TEXT13);
  65. /*              ": describe-key " */
  66.  
  67.     /* get the command sequence to describe
  68.        change it to something we can print as well */
  69.     cmdstr(c = getckey(FALSE), &outseq[0]);
  70.  
  71.     /* and dump it out */
  72.     ostring(outseq);
  73.     ostring(" ");
  74.  
  75.     /* find the right ->function */
  76.     if ((ptr = getfname(getbind(c))) == NULL)
  77.         ptr = "Not Bound";
  78.  
  79.     /* output the command sequence */
  80.     ostring(ptr);
  81. }
  82.  
  83. /* bindtokey:    add a new key to the key binding table        */
  84.  
  85. PASCAL NEAR bindtokey(f, n)
  86.  
  87. int f, n;    /* command arguments [IGNORED] */
  88.  
  89. {
  90.     register unsigned int c;/* command key to bind */
  91.     register int (PASCAL NEAR *kfunc)();/* ptr to the requested function to bind to */
  92.     register KEYTAB *ktp;    /* pointer into the command table */
  93.     register int found;    /* matched command flag */
  94.     char outseq[80];    /* output buffer for keystroke sequence */
  95.  
  96.     /* prompt the user to type in a key to bind */
  97.     /* get the function name to bind it to */
  98.     kfunc = getname(TEXT15);
  99. /*                      ": bind-to-key " */
  100.     if (kfunc == NULL) {
  101.         mlwrite(TEXT16);
  102. /*                      "[No such function]" */
  103.         return(FALSE);
  104.     }
  105.     ostring(" ");
  106.     TTflush();
  107.  
  108.     /* get the command sequence to bind */
  109.     c = getckey((kfunc == meta) || (kfunc == cex) ||
  110.             (kfunc == unarg) || (kfunc == ctrlg));
  111.  
  112.     /* change it to something we can print as well */
  113.     cmdstr(c, &outseq[0]);
  114.  
  115.     /* and dump it out */
  116.     ostring(outseq);
  117.  
  118.     /* if the function is a unique prefix key */
  119.     if (kfunc == unarg || kfunc == ctrlg) {
  120.  
  121.         /* search for an existing binding for the prefix key */
  122.         ktp = &keytab[0];
  123.         while (ktp->k_ptr.fp != NULL) {
  124.             if (ktp->k_ptr.fp == kfunc)
  125.                 unbindchar(ktp->k_code);
  126.             ++ktp;
  127.         }
  128.  
  129.         /* reset the appropriate global prefix variable */
  130.         if (kfunc == unarg)
  131.             reptc = c;
  132.         if (kfunc == ctrlg)
  133.             abortc = c;
  134.     }
  135.  
  136.     /* search the table to see if it exists */
  137.     ktp = &keytab[0];
  138.     found = FALSE;
  139.     while (ktp->k_ptr.fp != NULL) {
  140.         if (ktp->k_code == c) {
  141.             found = TRUE;
  142.             break;
  143.         }
  144.         ++ktp;
  145.     }
  146.  
  147.     if (found) {    /* it exists, just change it then */
  148.         ktp->k_ptr.fp = kfunc;
  149.         ktp->k_type = BINDFNC;
  150.     } else {    /* otherwise we need to add it to the end */
  151.         /* if we run out of binding room, bitch */
  152.         if (ktp >= &keytab[NBINDS]) {
  153.             mlwrite(TEXT17);
  154. /*                              "Binding table FULL!" */
  155.             return(FALSE);
  156.         }
  157.  
  158.         ktp->k_code = c;    /* add keycode */
  159.         ktp->k_ptr.fp = kfunc;    /* and the function pointer */
  160.         ktp->k_type = BINDFNC;    /* and the binding type */
  161.         ++ktp;            /* and make sure the next is null */
  162.         ktp->k_code = 0;
  163.         ktp->k_type = BINDNUL;
  164.         ktp->k_ptr.fp = NULL;
  165.     }
  166.  
  167.     /* if we have rebound the meta key, make the
  168.        search terminator follow it            */
  169.     if (kfunc == meta)
  170.         sterm = c;
  171.  
  172.     return(TRUE);
  173. }
  174.  
  175. /* macrotokey:    Bind a key to a macro in the key binding table */
  176.  
  177. PASCAL NEAR macrotokey(f, n)
  178.  
  179. int f, n;    /* command arguments [IGNORED] */
  180.  
  181. {
  182.     register unsigned int c;/* command key to bind */
  183.     register BUFFER *kmacro;/* ptr to buffer of macro to bind to key */
  184.     register KEYTAB *ktp;    /* pointer into the command table */
  185.     register int found;    /* matched command flag */
  186.     register int status;    /* error return */
  187.     char outseq[80];    /* output buffer for keystroke sequence */
  188.     char bufn[NBUFN];    /* buffer to hold macro name */
  189.  
  190.     /* get the buffer name to use */
  191.         if ((status=mlreply(TEXT215, &bufn[1], NBUFN-2)) != TRUE)
  192. /*              ": macro-to-key " */
  193.                 return(status);
  194.  
  195.     /* build the responce string for later */
  196.     strcpy(outseq, TEXT215);
  197. /*                  ": macro-to-key " */
  198.     strcat(outseq, &bufn[1]);
  199.  
  200.     /* translate it to a buffer pointer */
  201.     bufn[0] = '[';
  202.     strcat(bufn, "]");
  203.         if ((kmacro=bfind(bufn, FALSE, 0)) == NULL) {
  204.         mlwrite(TEXT130);
  205. /*        "Macro not defined"*/
  206.                 return(FALSE);
  207.         }
  208.  
  209.     strcat(outseq, " ");
  210.     mlwrite(outseq);
  211.  
  212.     /* get the command sequence to bind */
  213.     c = getckey(FALSE);
  214.  
  215.     /* change it to something we can print as well */
  216.     cmdstr(c, &outseq[0]);
  217.  
  218.     /* and dump it out */
  219.     ostring(outseq);
  220.  
  221.     /* search the table to see if it exists */
  222.     ktp = &keytab[0];
  223.     found = FALSE;
  224.     while (ktp->k_type != BINDNUL) {
  225.         if (ktp->k_code == c) {
  226.             found = TRUE;
  227.             break;
  228.         }
  229.         ++ktp;
  230.     }
  231.  
  232.     if (found) {    /* it exists, just change it then */
  233.         ktp->k_ptr.buf = kmacro;
  234.         ktp->k_type = BINDBUF;
  235.     } else {    /* otherwise we need to add it to the end */
  236.         /* if we run out of binding room, bitch */
  237.         if (ktp >= &keytab[NBINDS]) {
  238.             mlwrite(TEXT17);
  239. /*                              "Binding table FULL!" */
  240.             return(FALSE);
  241.         }
  242.  
  243.         ktp->k_code = c;    /* add keycode */
  244.         ktp->k_ptr.buf = kmacro;    /* and the function pointer */
  245.         ktp->k_type = BINDBUF;    /* and the binding type */
  246.         ++ktp;            /* and make sure the next is null */
  247.         ktp->k_code = 0;
  248.         ktp->k_type = BINDNUL;
  249.         ktp->k_ptr.fp = NULL;
  250.     }
  251.  
  252.     return(TRUE);
  253. }
  254.  
  255. /* unbindkey:    delete a key from the key binding table    */
  256.  
  257. PASCAL NEAR unbindkey(f, n)
  258.  
  259. int f, n;    /* command arguments [IGNORED] */
  260.  
  261. {
  262.     register int c;        /* command key to unbind */
  263.     char outseq[80];    /* output buffer for keystroke sequence */
  264.  
  265.     /* prompt the user to type in a key to unbind */
  266.     mlwrite(TEXT18);
  267. /*              ": unbind-key " */
  268.  
  269.     /* get the command sequence to unbind */
  270.     c = getckey(FALSE);        /* get a command sequence */
  271.  
  272.     /* change it to something we can print as well */
  273.     cmdstr(c, &outseq[0]);
  274.  
  275.     /* and dump it out */
  276.     ostring(outseq);
  277.  
  278.     /* if it isn't bound, bitch */
  279.     if (unbindchar(c) == FALSE) {
  280.         mlwrite(TEXT19);
  281. /*                      "[Key not bound]" */
  282.         return(FALSE);
  283.     }
  284.     return(TRUE);
  285. }
  286.  
  287. PASCAL NEAR unbindchar(c)
  288.  
  289. int c;        /* command key to unbind */
  290.  
  291. {
  292.     register KEYTAB *ktp;    /* pointer into the command table */
  293.     register KEYTAB *sktp;    /* saved pointer into the command table */
  294.     register int found;    /* matched command flag */
  295.  
  296.     /* search the table to see if the key exists */
  297.     ktp = &keytab[0];
  298.     found = FALSE;
  299.     while (ktp->k_type != BINDNUL) {
  300.         if (ktp->k_code == c) {
  301.             found = TRUE;
  302.             break;
  303.         }
  304.         ++ktp;
  305.     }
  306.  
  307.     /* if it isn't bound, bitch */
  308.     if (!found)
  309.         return(FALSE);
  310.  
  311.     /* save the pointer and scan to the end of the table */
  312.     sktp = ktp;
  313.     while (ktp->k_ptr.fp != NULL)
  314.         ++ktp;
  315.     --ktp;        /* backup to the last legit entry */
  316.  
  317.     /* copy the last entry to the current one */
  318.     sktp->k_code = ktp->k_code;
  319.     sktp->k_type = ktp->k_type;
  320.     sktp->k_ptr.fp   = ktp->k_ptr.fp;
  321.  
  322.     /* null out the last one */
  323.     ktp->k_code = 0;
  324.     ktp->k_type = BINDNUL;
  325.     ktp->k_ptr.fp = NULL;
  326.     return(TRUE);
  327. }
  328.  
  329. /* Describe bindings:
  330.  
  331.        bring up a fake buffer and list the key bindings
  332.        into it with view mode
  333. */
  334.  
  335. PASCAL NEAR desbind(f, n)
  336.  
  337. #if    APROP
  338. {
  339.     return(buildlist(TRUE, ""));
  340. }
  341.  
  342. PASCAL NEAR apro(f, n)    /* Apropos (List functions that match a substring) */
  343.  
  344. {
  345.     char mstring[NSTRING];    /* string to match cmd names to */
  346.     int status;        /* status return */
  347.  
  348.     status = mlreply(TEXT20, mstring, NSTRING - 1);
  349. /*                       "Apropos string: " */
  350.     if (status != TRUE)
  351.         return(status);
  352.  
  353.     return(buildlist(FALSE, mstring));
  354. }
  355.  
  356. PASCAL NEAR buildlist(type, mstring)  /* build a binding list (limited or full) */
  357.  
  358. int type;    /* true = full list,   false = partial list */
  359. char *mstring;    /* match string if a partial list */
  360.  
  361. #endif
  362. {
  363.     register WINDOW *wp;    /* scanning pointer to windows */
  364.     register KEYTAB *ktp;    /* pointer into the command table */
  365.     register NBIND *nptr;    /* pointer into the name binding table */
  366.     register BUFFER *bp;    /* buffer to put binding list into */
  367.     int cpos;        /* current position to use in outseq */
  368.     int cmark;        /* current mark */
  369.     char outseq[80];    /* output buffer for keystroke sequence */
  370.  
  371.     /* split the current window to make room for the binding list */
  372.     if (splitwind(FALSE, 1) == FALSE)
  373.             return(FALSE);
  374.  
  375.     /* and get a buffer for it */
  376.     bp = bfind(TEXT21, TRUE, 0);
  377. /*                 "Binding list" */
  378.     if (bp == NULL || bclear(bp) == FALSE) {
  379.         mlwrite(TEXT22);
  380. /*                      "Can not display binding list" */
  381.         return(FALSE);
  382.     }
  383.  
  384.     /* let us know this is in progress */
  385.     mlwrite(TEXT23);
  386. /*              "[Building binding list]" */
  387.  
  388.     /* disconect the current buffer */
  389.         if (--curbp->b_nwnd == 0) {             /* Last use.            */
  390.                 curbp->b_dotp  = curwp->w_dotp;
  391.                 curbp->b_doto  = curwp->w_doto;
  392.         for (cmark = 0; cmark < NMARKS; cmark++) {
  393.                     curbp->b_markp[cmark] = curwp->w_markp[cmark];
  394.                     curbp->b_marko[cmark] = curwp->w_marko[cmark];
  395.             }
  396.         curbp->b_fcol  = curwp->w_fcol;
  397.         }
  398.  
  399.     /* connect the current window to this buffer */
  400.     curbp = bp;    /* make this buffer current in current window */
  401.     bp->b_mode = 0;        /* no modes active in binding list */
  402.     bp->b_nwnd++;        /* mark us as more in use */
  403.     wp = curwp;
  404.     wp->w_bufp = bp;
  405.     wp->w_linep = bp->b_linep;
  406.     wp->w_flag = WFHARD|WFFORCE;
  407.     wp->w_dotp = bp->b_dotp;
  408.     wp->w_doto = bp->b_doto;
  409.     for (cmark = 0; cmark < NMARKS; cmark++) {
  410.         wp->w_markp[cmark] = NULL;
  411.         wp->w_marko[cmark] = 0;
  412.     }
  413.  
  414.     /* build the contents of this window, inserting it line by line */
  415.     nptr = &names[0];
  416.     while (nptr->n_func != NULL) {
  417.  
  418.         /* add in the command name */
  419.         strcpy(outseq, nptr->n_name);
  420.         cpos = strlen(outseq);
  421.  
  422. #if    APROP
  423.         /* if we are executing an apropos command..... */
  424.         if (type == FALSE &&
  425.             /* and current string doesn't include the search string */
  426.             strinc(outseq, mstring) == FALSE)
  427.             goto fail;
  428. #endif
  429.         /* search down any keys bound to this */
  430.         ktp = &keytab[0];
  431.         while (ktp->k_type != BINDNUL) {
  432.             if (ktp->k_type == BINDFNC &&
  433.                 ktp->k_ptr.fp == nptr->n_func) {
  434.                 /* padd out some spaces */
  435.                 while (cpos < 25)
  436.                     outseq[cpos++] = ' ';
  437.  
  438.                 /* add in the command sequence */
  439.                 cmdstr(ktp->k_code, &outseq[cpos]);
  440.                 strcat(outseq, "\r");
  441.  
  442.                 /* and add it as a line into the buffer */
  443.                 if (linstr(outseq) != TRUE)
  444.                     return(FALSE);
  445.  
  446.                 cpos = 0;    /* and clear the line */
  447.             }
  448.             ++ktp;
  449.         }
  450.  
  451.         /* if no key was bound, we need to dump it anyway */
  452.         if (cpos > 0) {
  453.             outseq[cpos++] = '\r';
  454.             outseq[cpos] = 0;
  455.             if (linstr(outseq) != TRUE)
  456.                 return(FALSE);
  457.         }
  458.  
  459. fail:        /* and on to the next name */
  460.         ++nptr;
  461.     }
  462.  
  463.     /* add a blank line between the key and macro lists */
  464.     lnewline();
  465.  
  466.     /* scan all buffers looking for macroes and their bindings */
  467.     bp = bheadp;
  468.     while (bp) {
  469.  
  470.         /* is this buffer a macro? */
  471.         if (bp->b_bname[0] != '[')
  472.             goto bfail;
  473.  
  474.         /* add in the command name */
  475.         strcpy(outseq, bp->b_bname);
  476.         cpos = strlen(outseq);
  477.  
  478. #if    APROP
  479.         /* if we are executing an apropos command..... */
  480.         if (type == FALSE &&
  481.             /* and current string doesn't include the search string */
  482.             strinc(outseq, mstring) == FALSE)
  483.             goto bfail;
  484. #endif
  485.         /* search down any keys bound to this macro */
  486.         ktp = &keytab[0];
  487.         while (ktp->k_ptr.fp != NULL) {
  488.             if (ktp->k_type == BINDBUF &&
  489.                 ktp->k_ptr.buf == bp) {
  490.                 /* padd out some spaces */
  491.                 while (cpos < 25)
  492.                     outseq[cpos++] = ' ';
  493.  
  494.                 /* add in the command sequence */
  495.                 cmdstr(ktp->k_code, &outseq[cpos]);
  496.                 strcat(outseq, "\r");
  497.  
  498.                 /* and add it as a line into the buffer */
  499.                 if (linstr(outseq) != TRUE)
  500.                     return(FALSE);
  501.  
  502.                 cpos = 0;    /* and clear the line */
  503.             }
  504.             ++ktp;
  505.         }
  506.  
  507.         /* if no key was bound, we need to dump it anyway */
  508.         if (cpos > 0) {
  509.             outseq[cpos++] = '\r';
  510.             outseq[cpos] = 0;
  511.             if (linstr(outseq) != TRUE)
  512.                 return(FALSE);
  513.         }
  514.  
  515. bfail:        /* and on to the next buffer */
  516.         bp = bp->b_bufp;
  517.     }
  518.  
  519.     curwp->w_bufp->b_mode |= MDVIEW;/* put this buffer view mode */
  520.     curbp->b_flag &= ~BFCHG;    /* don't flag this as a change */
  521.     wp->w_dotp = lforw(curbp->b_linep);/* back to the beginning */
  522.     wp->w_doto = 0;
  523.     upmode();
  524.     mlwrite("");    /* clear the mode line */
  525.     return(TRUE);
  526. }
  527.  
  528. #if    APROP
  529. PASCAL NEAR strinc(source, sub)    /* does source include sub? */
  530.  
  531. char *source;    /* string to search in */
  532. char *sub;    /* substring to look for */
  533.  
  534. {
  535.     char *sp;    /* ptr into source */
  536.     char *nxtsp;    /* next ptr into source */
  537.     char *tp;    /* ptr into substring */
  538.  
  539.     /* for each character in the source string */
  540.     sp = source;
  541.     while (*sp) {
  542.         tp = sub;
  543.         nxtsp = sp;
  544.  
  545.         /* is the substring here? */
  546.         while (*tp) {
  547.             if (*nxtsp++ != *tp)
  548.                 break;
  549.             else
  550.                 tp++;
  551.         }
  552.  
  553.         /* yes, return a success */
  554.         if (*tp == 0)
  555.             return(TRUE);
  556.  
  557.         /* no, onward */
  558.         sp++;
  559.     }
  560.     return(FALSE);
  561. }
  562. #endif
  563.  
  564. /* get a command key sequence from the keyboard    */
  565.  
  566. unsigned int PASCAL NEAR getckey(mflag)
  567.  
  568. int mflag;    /* going for a meta sequence? */
  569.  
  570. {
  571.     register unsigned int c;    /* character fetched */
  572.     char tok[NSTRING];        /* command incoming */
  573.  
  574.     /* check to see if we are executing a command line */
  575.     if (clexec) {
  576.         macarg(tok);    /* get the next token */
  577.         return(stock(tok));
  578.     }
  579.  
  580.     /* or the normal way */
  581.     if (mflag)
  582.         c = getkey();
  583.     else
  584.         c = getcmd();
  585.     return(c);
  586. }
  587.  
  588. /* execute the startup file */
  589.  
  590. PASCAL NEAR startup(sfname)
  591.  
  592. char *sfname;    /* name of startup file (null if default) */
  593.  
  594. {
  595.     char *fname;    /* resulting file name to execute */
  596.  
  597.     /* look up the startup file */
  598.     if (*sfname != 0)
  599.         fname = flook(sfname, TRUE);
  600.     else
  601. #if SHARED
  602.     {
  603.             strcpy(tname, pathname[0]);
  604.             fname = flook(tname, TRUE);
  605.         }
  606. #else
  607.         fname = flook(pathname[0], TRUE);
  608. #endif
  609.  
  610.     /* if it isn't around, don't sweat it */
  611.     if (fname == NULL)
  612.         return(TRUE);
  613.  
  614.     /* otherwise, execute the sucker */
  615.     return(dofile(fname));
  616. }
  617.  
  618. /*    Look up the existance of a file along the normal or PATH
  619.     environment variable. Look first in the HOME directory if
  620.     asked and possible
  621. */
  622.  
  623. char *PASCAL NEAR flook(fname, hflag)
  624.  
  625. char *fname;    /* base file name to search for */
  626. int hflag;    /* Look in the HOME environment variable first? */
  627.  
  628. {
  629.     register char *home;    /* path to home directory */
  630.     register char *path;    /* environmental PATH variable */
  631.     register char *sp;    /* pointer into path spec */
  632.     register int i;        /* index */
  633.     static char fspec[NFILEN];    /* full path spec to search */
  634.     char *getenv();
  635.  
  636.     /* if we have an absolute path.. check only there! */
  637.     sp = fname;
  638.     while (*sp) {
  639.         if (*sp == ':' || *sp == '\\' || *sp == '/') {
  640.             if (ffropen(fname) == FIOSUC) {
  641.                 ffclose();
  642.                 return(fname);
  643.             } else
  644.                 return(NULL);
  645.         }
  646.         ++sp;
  647.     }
  648.  
  649. #if    ENVFUNC
  650.  
  651.     if (hflag) {
  652. #if WMCS
  653.         home = getenv("SYS$HOME");
  654. #else
  655.         home = getenv("HOME");
  656. #endif
  657.         if (home != NULL) {
  658.             /* build home dir file spec */
  659.             strcpy(fspec, home);
  660. #if WMCS
  661.             strcat(fspec,fname);
  662. #else
  663.             strcat(fspec, "/");
  664.             strcat(fspec, fname);
  665. #endif
  666.  
  667.             /* and try it out */
  668.             if (ffropen(fspec) == FIOSUC) {
  669.                 ffclose();
  670.                 return(fspec);
  671.             }
  672.         }
  673.     }
  674. #endif
  675.  
  676.     /* always try the current directory first */
  677.     if (ffropen(fname) == FIOSUC) {
  678.         ffclose();
  679.         return(fname);
  680.     }
  681.  
  682. #if    ENVFUNC
  683.     /* get the PATH variable */
  684. #if WMCS
  685.     path = getenv("OPT$PATH");
  686. #else
  687.     path = getenv("PATH");
  688. #endif
  689.     if (path != NULL)
  690.         while (*path) {
  691.  
  692.             /* build next possible file spec */
  693.             sp = fspec;
  694. #if    ST520 & MWC
  695.             while (*path && (*path != PATHCHR) && (*path != ','))
  696. #else
  697.             while (*path && (*path != PATHCHR))
  698. #endif
  699.                 *sp++ = *path++;
  700.  
  701.             /* add a terminating dir separator if we need it */
  702.             if (*(sp-1) != DIRSEPCHAR)
  703.                 *sp++ = DIRSEPCHAR;
  704.             *sp = 0;
  705.             strcat(fspec, fname);
  706.  
  707.             /* and try it out */
  708.             if (ffropen(fspec) == FIOSUC) {
  709.                 ffclose();
  710.                 return(fspec);
  711.             }
  712.  
  713. #if    ST520 & MWC
  714.             if ((*path == PATHCHR) || (*path == ','))
  715. #else
  716.             if (*path == PATHCHR)
  717. #endif
  718.                 ++path;
  719.         }
  720. #endif
  721.  
  722.     /* look it up via the old table method */
  723.     for (i=2; i < NPNAMES; i++) {
  724.         strcpy(fspec, pathname[i]);
  725.         strcat(fspec, fname);
  726.  
  727.         /* and try it out */
  728.         if (ffropen(fspec) == FIOSUC) {
  729.             ffclose();
  730.             return(fspec);
  731.         }
  732.     }
  733.  
  734.     return(NULL);    /* no such luck */
  735. }
  736.  
  737. PASCAL NEAR cmdstr(c, seq) /* change a key command to a string we can print out */
  738.  
  739. int c;        /* sequence to translate */
  740. char *seq;    /* destination string for sequence */
  741.  
  742. {
  743.     char *ptr;    /* pointer into current position in sequence */
  744.  
  745.     ptr = seq;
  746.  
  747.     /* apply ^X sequence if needed */
  748.     if (c & CTLX) {
  749.         *ptr++ = '^';
  750.         *ptr++ = 'X';
  751.     }
  752.  
  753.     /* apply ALT key sequence if needed */
  754.     if (c & ALTD) {
  755.         *ptr++ = 'A';
  756.         *ptr++ = '-';
  757.     }
  758.  
  759.     /* apply Shifted sequence if needed */
  760.     if (c & SHFT) {
  761.         *ptr++ = 'S';
  762.         *ptr++ = '-';
  763.     }
  764.  
  765.     /* apply MOUS sequence if needed */
  766.     if (c & MOUS) {
  767.         *ptr++ = 'M';
  768.         *ptr++ = 'S';
  769.     }
  770.  
  771.     /* apply meta sequence if needed */
  772.     if (c & META) {
  773.         *ptr++ = 'M';
  774.         *ptr++ = '-';
  775.     }
  776.  
  777.     /* apply SPEC sequence if needed */
  778.     if (c & SPEC) {
  779.         *ptr++ = 'F';
  780.         *ptr++ = 'N';
  781.     }
  782.  
  783.     /* apply control sequence if needed */
  784.     if (c & CTRL) {
  785.         *ptr++ = '^';
  786.     }
  787.  
  788.     c = c & 255;    /* strip the prefixes */
  789.  
  790.     /* and output the final sequence */
  791.  
  792.     *ptr++ = c;
  793.     *ptr = 0;    /* terminate the string */
  794. }
  795.  
  796. /*    This function looks a key binding up in the binding table    */
  797.  
  798. KEYTAB *getbind(c)
  799.  
  800. int c;    /* key to find what is bound to it */
  801.  
  802. {
  803.     register KEYTAB *ktp;
  804.  
  805.     /* scan through the binding table, looking for the key's entry */
  806.         ktp = &keytab[0];
  807.         while (ktp->k_type != BINDNUL) {
  808.                 if (ktp->k_code == c)
  809.                         return(ktp);
  810.                 ++ktp;
  811.         }
  812.  
  813.     /* no such binding */
  814.     return((KEYTAB *)NULL);
  815. }
  816.  
  817. /* getfname:    This function takes a ptr to KEYTAB entry and gets the name
  818.         associated with it
  819. */
  820.  
  821. char *PASCAL NEAR getfname(key)
  822.  
  823. KEYTAB *key;    /* key binding to return a name of */
  824.  
  825. {
  826.     int (PASCAL NEAR *func)(); /* ptr to the requested function */
  827.     register NBIND *nptr;    /* pointer into the name binding table */
  828.     register BUFFER *bp;    /* ptr to buffer to test */
  829.     register BUFFER *kbuf;    /* ptr to requested buffer */
  830.  
  831.     /* if this isn't a valid key, it has no name */
  832.     if (key == NULL)
  833.         return(NULL);
  834.  
  835.     /* skim through the binding table, looking for a match */
  836.     if (key->k_type == BINDFNC) {
  837.         func = key->k_ptr.fp;
  838.         nptr = &names[0];
  839.         while (nptr->n_func != NULL) {
  840.             if (nptr->n_func == func)
  841.                 return(nptr->n_name);
  842.             ++nptr;
  843.         }
  844.         return(NULL);
  845.     }
  846.  
  847.     /* skim through the buffer list looking for a match */
  848.     kbuf = key->k_ptr.buf;
  849.     bp = bheadp;
  850.     while (bp) {
  851.         if (bp == kbuf)
  852.             return(bp->b_bname);
  853.         bp = bp->b_bufp;
  854.     }
  855.     return(NULL);
  856. }
  857.  
  858. /* fncmatch:    match fname to a function in the names table and return
  859.         any match or NULL if none */
  860.  
  861. int (PASCAL NEAR *PASCAL NEAR fncmatch(fname))()
  862.  
  863. char *fname;    /* name to attempt to match */
  864.  
  865. {
  866. #if    BINARY
  867.     register int nval;    /* value of matched name */
  868.  
  869.     nval = binary(fname, namval, numfunc);
  870.     if (nval == -1)
  871.         return(NULL);
  872.     else
  873.         return(names[nval].n_func);
  874. #else
  875.     register NBIND *ffp;    /* pointer to entry in name binding table */
  876.  
  877.     /* scan through the table, returning any match */
  878.     ffp = &names[0];
  879.     while (ffp->n_func != NULL) {
  880.         if (strcmp(fname, ffp->n_name) == 0)
  881.             return(ffp->n_func);
  882.         ++ffp;
  883.     }
  884.     return(NULL);
  885. #endif
  886. }
  887.  
  888. #if    BINARY
  889. char *PASCAL NEAR namval(index)
  890.  
  891. int index;    /* index of name to fetch out of the name table */
  892.  
  893. {
  894.     return(names[index].n_name);
  895. }
  896. #endif
  897.  
  898. /*    stock()        String key name TO Command Key
  899.  
  900.     A key binding consists of one or more prefix functions followed by
  901.     a keystroke.  Allowable prefixes must be in the following order:
  902.  
  903.     ^X    preceeding control-X
  904.     A-    simeltaneous ALT key (on PCs mainly)
  905.     S-    shifted function key
  906.     MS    mouse generated keystroke
  907.     M-    Preceding META key
  908.     FN    function key
  909.     ^    control key
  910.  
  911.     Meta and ^X prefix of lower case letters are converted to upper
  912.     case.  Real control characters are automatically converted to
  913.     the ^A form.
  914. */
  915.  
  916. unsigned int PASCAL NEAR stock(keyname)
  917.  
  918. char *keyname;    /* name of key to translate to Command key form */
  919.  
  920. {
  921.     register unsigned int c;    /* key sequence to return */
  922.  
  923.     /* parse it up */
  924.     c = 0;
  925.  
  926.     /* Do ^X prefix */
  927.     if(*keyname == '^' && *(keyname+1) == 'X') {
  928.         if(*(keyname+2) != 0) { /* Key is not bare ^X */
  929.             c |= CTLX;
  930.             keyname += 2;
  931.         }
  932.     }
  933.  
  934.     /* and the ALT key prefix */
  935.     if (*keyname == 'A' && *(keyname+1) == '-') {
  936.         c |= ALTD;
  937.         keyname += 2;
  938.     }
  939.  
  940.     /* and the SHIFTED prefix */
  941.     if (*keyname == 'S' && *(keyname+1) == '-') {
  942.         c |= SHFT;
  943.         keyname += 2;
  944.     }
  945.  
  946.     /* and the mouse (MOUS) prefix */
  947.     if (*keyname == 'M' && *(keyname+1) == 'S') {
  948.         c |= MOUS;
  949.         keyname += 2;
  950.     }
  951.  
  952.     /* then the META prefix */
  953.     if (*keyname == 'M' && *(keyname+1) == '-') {
  954.         c |= META;
  955.         keyname += 2;
  956.     }
  957.  
  958.     /* next the function prefix */
  959.     if (*keyname == 'F' && *(keyname+1) == 'N') {
  960.         c |= SPEC;
  961.         keyname += 2;
  962.     }
  963.  
  964.     /* a control char?  (Always upper case) */
  965.     if (*keyname == '^' && *(keyname+1) != 0) {
  966.         c |= CTRL;
  967.         ++keyname;
  968.         uppercase(keyname);
  969.     }
  970.  
  971.     /* A literal control character? (Boo, hiss) */
  972.     if (*keyname < 32) {
  973.         c |= CTRL;
  974.         *keyname += '@';
  975.     }
  976.  
  977.     /* make sure we are not lower case if used with ^X or M- */
  978.     if(!(c & (MOUS|SPEC|ALTD|SHFT)))    /* If not a special key */
  979.         if( c & (CTLX|META))        /* If is a prefix */
  980.         uppercase(keyname);        /* Then make sure it's upper case */
  981.  
  982.     /* the final sequence... */
  983.     c |= *keyname;
  984.     return(c);
  985. }
  986.  
  987. char *PASCAL NEAR transbind(skey)    /* string key name to binding name.... */
  988.  
  989. char *skey;    /* name of key to get binding for */
  990.  
  991. {
  992.     char *bindname;
  993.  
  994.     bindname = getfname(getbind(stock(skey)));
  995.     if (bindname == NULL)
  996.         bindname = errorm;
  997.  
  998.     return(bindname);
  999. }
  1000.  
  1001. int PASCAL NEAR execkey(key, f, n)    /* execute a function bound to a key */
  1002.  
  1003. KEYTAB *key;    /* key to execute */
  1004. int f, n;    /* agruments to C function */
  1005.  
  1006. {
  1007.     register int status;    /* error return */
  1008.  
  1009.     if (key->k_type == BINDFNC)
  1010.         return((*(key->k_ptr.fp))(f, n));
  1011.     if (key->k_type == BINDBUF) {
  1012.         while (n--) {
  1013.             status = dobuf(key->k_ptr.buf);
  1014.             if (status != TRUE)
  1015.                 return(status);
  1016.         }
  1017.     }
  1018.     return(TRUE);
  1019. }
  1020.  
  1021. /* set a KEYTAB to the given name of the given type */
  1022.  
  1023. setkey(key, type, name)
  1024.  
  1025. KEYTAB *key;        /* ptr to key to set */
  1026. short type;        /* type of binding */
  1027. char *name;        /* name of function or buffer */
  1028.  
  1029. {
  1030.     key->k_type = type;
  1031.     if (type == BINDFNC)
  1032.         key->k_ptr.fp = fncmatch(name);
  1033.     else if (type == BINDBUF)
  1034.         /* not quite yet... */;
  1035. }
  1036.